/**
 * 
 */
package de.fsujena.chat_pi.server.user;

import java.io.*;
import java.util.*;

import de.fsujena.chat_pi.message.SysMessage;
import de.fsujena.chat_pi.message.SysMessage.CATEGORY;
import de.fsujena.chat_pi.message.SysMessage.MESSAGE;
import de.fsujena.chat_pi.server.Server;
import de.fsujena.chat_pi.server.user.User.ROLE;
import de.fsujena.chat_pi.server.user.User.STATUS;

/**
 * @author Erik Hebestreit
 *
 */
public class UserManager {
	public HashMap<String,User> users = new HashMap<String,User>();
	public String filename = "userMap.dat";
	Server serverObject;
	
	/**
	 * initialize UserManager
	 */
	public UserManager(Server serverObject) {
		this.serverObject = serverObject;
		
		loadUserMap();
	}

	/**
	 * Load the user map containing the informations of all users on the server.
	 * 
	 * @return true if successful, false if not
	 */
	@SuppressWarnings("unchecked")
	public boolean loadUserMap() {
		// load existing users from database
		File file = new File(filename);
		
		if (file.exists()) {
			try {
				FileInputStream f = new FileInputStream(file);
				ObjectInputStream s = new ObjectInputStream(f);
				Object inputObject = s.readObject();
				
				if (inputObject instanceof HashMap<?,?>)
					this.users = (HashMap<String,User>) inputObject;
				
				s.close();
				return true;
			} catch (IOException e) {
				e.printStackTrace();
				return false;
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
				return false;
			}
		} else {
			System.err.println("Failed to load users map.");
			return false;
		}		
	}
	
	/**
	 * method to create a new user from name and password
	 * 
	 * @return name of the user just created
	 */
	public String createUser(String name,String password) {
		//TODO: if not successful return false!?
		
		users.put(name,new User(name,password));
		this.saveUserMap();
		return name;
	}
	
	/**
	 * 
	 * @param name user name to be searched for
	 * @return user entry if user exists, <code>null</code> if user doesn't exist
	 */
	private User getUser(String name) {
		return users.get(name);
	}
	
	/**
	 * check if a certain entry exists
	 * 
	 * @param name user name to be searched for
	 * @return <b>true</b> if the entry exists, <b>false</b> if it does not exist
	 */
	public Boolean checkUserExists(String name) {
		// TODO: also check case-insensitive
		return users.containsKey(name);
	}
	
	/**
	 * check the if the name-password combination is right
	 * 
	 * @param name user name to be searched for
	 * @param password password to be checked
	 * @return <b>true</b> if password is right, <b>false</b> if not
	 */
	public Boolean checkUserPassword(String name,String password) {
		return this.getUser(name).checkPassword(password);
	}
	
	/**
	 * changes the user name of a user with password authentication
	 * 
	 * @param name current user name
	 * @param password password of user
	 * @param newName new user name
	 * @return <b>true</b> if successful, <b>false</b> if not
	 */
	public Boolean changeUserName(String name,String password,String newName) {
		// check if password is right
		if (!this.checkUserPassword(name, password))
			return false;
		
		// set new name in user entry
		if (!this.getUser(name).setName(newName))
			return false;
		
		// put new key-value pair in HashMap to match new name
		users.put(newName, this.getUser(newName));
		// remove old HashMap entry
		users.remove(name);
		
		this.saveUserMap();
		
		return this.checkUserExists(newName);
	}
	
	/**
	 * change the password of a certain user
	 * 
	 * @param username
	 * @param oldPassword
	 * @param newPassword
	 * @return true if successful, false if not (user does not exist, or old password wrong)
	 */
	public boolean changeUserPassword(String username, String oldPassword, String newPassword) {
		if(!checkUserExists(username))
			return false;
		
		return this.getUser(username).setPassword(oldPassword, newPassword);
	}
	
	/**
	 * remove a user
	 * 
	 * @param username of the user to be removed
	 * @return true if user existed and is now removed, false if username was not found
	 */
	public boolean removeUser(String username) {
		// first check, if the user exists
		if (checkUserExists(username)) {
			// check, if user is logged in
			int userNum = serverObject.networkListener.userExists(username);
			
			// if logged in, send the user a message and remove the client
			if (userNum >= 0) {
				serverObject.networkListener.messageListener.get(userNum).sendSysMessage(CATEGORY.STATUS, MESSAGE.YOU_ARE_REMOVED);
				serverObject.networkListener.removeClient(username);
			}
			
			// remove the user from the user map
			users.remove(username);
			
			// save changes
			saveUserMap();
			
			return true;
		} else {
			return false;
		}
	}
	
	/**
     * kicks user from server
     * 
     * @param name username to be kicked
     */
    public void kickUser(String name){
            SysMessage msg= new SysMessage(CATEGORY.MODERATE,MESSAGE.KICK_USER,name);
            msg.setSent(new Date());
            msg.setSenderUsername(name);
            serverObject.networkListener.distributeMsg(msg);
            serverObject.networkListener.removeClient(name);
            return;
    }
	
	/**
	 * banish a user from the server
	 * 
	 * @param username
	 * @return true if user exists and is banished, false if username was not found
	 */
	public boolean bannUser(String username) {
		if (checkUserExists(username)) {
			int userNum = serverObject.networkListener.userExists(username);
			if (userNum >= 0) {
				SysMessage msg= new SysMessage(CATEGORY.MODERATE,MESSAGE.BANN_USER,username);
				msg.setSent(new Date());
				msg.setSenderUsername(username);
				serverObject.networkListener.distributeMsg(msg);
				serverObject.networkListener.removeClient(username);
			}
			
			users.get(username).status = STATUS.BANNED;
			saveUserMap();
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * unbanish a user
	 * 
	 * @param username
	 * @return true if user exists and its status is set to normal, false if username was not found
	 */
	public boolean unbannUser(String username) {
		if (checkUserExists(username)) {
			users.get(username).status = STATUS.NORMAL;
			saveUserMap();
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * save user map to file
	 * 
	 * @return true if successful, false if not
	 */
	public boolean saveUserMap() {
		File file = new File(filename);
		FileOutputStream f;
		ObjectOutputStream s;
		
		try {
			f = new FileOutputStream(file);
			s = new ObjectOutputStream(f);
		} catch (FileNotFoundException e1) {
			e1.printStackTrace();
			return false;
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		
		try {
			s.writeObject(this.users);
			s.close();
		} catch (IOException e) {
			e.printStackTrace();
			return false;
		}
		
		return true;
	}
	
	/**
	 * Check the current status of a given user.
	 * 
	 * @param username
	 * @return STATUS variable, as defined in User.STATUS
	 */
	public STATUS checkUserStatus(String username) {
		if (checkUserExists(username)) {
			return getUser(username).status;
		} else {
			return null; // TODO: return null is not cool
		}
	}
	
	// TODO: setUserStatus --> isn't this needed somewhere and thus should be implemented?

	/**
	 * Check the current role of a given user.
	 * 
	 * @param username
	 * @return ROLE variable, as defined in User.ROLE
	 */
	public ROLE checkUserRole(String username) {
		if (checkUserExists(username)) {
			return getUser(username).role;
		} else {
			return null; // TODO: return null is not cool
		}
	}
	
	/**
	 * Set the role of a certain user.
	 * 
	 * @param username
	 * @param role as defined in User.ROLE
	 * @return true if user existed and role was changed, false if user does not exist
	 */
	public boolean setUserRole(String username, ROLE role) {
		if (checkUserExists(username)) {
			getUser(username).role = role;
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * Generate a list of all admins.
	 * 
	 * @return returns a list of the admins that are enlisted on the server
	 */
	public String[] adminlist(){
		/*int number_of_admins=0;
		for(String name : users.keySet()){
			if(users.get(name).role==ROLE.ADMIN)number_of_admins++;					//at first we need the number of admins
		}
		String[] adminlist = new String[number_of_admins];
		int i=0;
		for(String name : users.keySet()){
			
			if(users.get(name).role==ROLE.ADMIN)adminlist[i]=users.get(name).name;	//here we get the names
			i++;
		}*/
		
		String[] adminlist = new String[1];
		String[] tempadminlist = new String[1];
		
		int i=0;
		for(String name : users.keySet()){
			
			if(users.get(name).role==ROLE.ADMIN){				// check if the username is an admin
				tempadminlist=adminlist.clone();				//save the list
				adminlist = new String[i+1];					//set the new size of the list
				adminlist=tempadminlist.clone();				//copy the list back
				tempadminlist = new String[i+1];				
				adminlist[i]=users.get(name).name;				//here we get the names
			i++;
		}
		}
	
		
		return adminlist;															//and return them
	}
	
	/**
	 * Generate a list of all moderators.
	 * 
	 * @return returns a list of the mods that are enlisted on the server
	 */
	public String[] modlist(){
		/*int number_of_mods=0;
		for(String name : users.keySet()){
			if(users.get(name).role==ROLE.MODERATOR)number_of_mods++;					//at first we need the number of mods
		}
		String[] modlist = new String[number_of_mods];
		int i=0;
		for(String name : users.keySet()){
			
			if(users.get(name).role==ROLE.ADMIN)modlist[i]=users.get(name).name;	//here we get the names
			i++;
		}*/
		
		String[] modlist = new String[1];
		String[] tempmodlist = new String[1];
		
		int i=0;
		for(String name : users.keySet()){
			
			if(users.get(name).role==ROLE.MODERATOR){			// check if the username is an mod
				tempmodlist=modlist.clone();				//save the list
				modlist = new String[i+1];					//set the new size of the list
				modlist=tempmodlist.clone();				//copy the list back
				tempmodlist = new String[i+1];				
				modlist[i]=users.get(name).name;				//here we get the names
			i++;
		}
		}
		return modlist;															//and return them
	}
}
